package com.example.spring.code.a01;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger;

@SpringBootApplication
public class A01Application {

    private static final Logger log = Logger.getLogger(A01Application.class.getName());


    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);

        ApplicationContext applicationContext;
        /*
            1. 到底什么是 BeanFactory
                - 它是 ApplicationContext 的父接口
                - 它才是 Spring 的核心容器，主要的 ApplicationContext 实现都【组合】了它的功能（BeanFactory 是 ApplicationContext 的一个成员变量）
         */

        /*
            2. BeanFactory 能干点啥
                - 表面上只有 getBean
                - 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能，都由它的实现类提供 如 DefaultListableBeanFactory
         */
        /*
            DefaultSingletonBeanRegistry 是 DefaultListableBeanFactory 的父类，DefaultListableBeanFactory 继承了它
                - singletonObjects 是 DefaultSingletonBeanRegistry 的一个成员变量
                - singletonObjects 存储了所有单例 Bean
         */
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
                .forEach(e -> {
                    System.out.println(e.getKey() + "=" + e.getValue());
                });

        /*
            3. ApplicationContext 比BeanFactory 多了哪些功能呢？
                - 1. 处理国际化资源 MessageSource
                - 2. 通配符匹配资源 ResourcePatternResolver
                - 3. 发布事件对象  ApplicationEventPublisher
                - 4. 获取环境信息  EnvironmentCapable
         */
        // -1. 处理国际化资源
        System.out.println(context.getMessage("hi", null, Locale.CHINA));
        System.out.println(context.getMessage("hi", null, Locale.US));
        System.out.println(context.getMessage("hi", null, Locale.JAPAN));
        // -2. 通配符匹配资源
        Resource[] resources = context.getResources("classpath:application.properties");
        for (Resource resource : resources) {
            System.out.println(resource.getDescription());
        }
        Resource[] resources1 = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources1) {
            System.out.println(resource.getDescription());
        }

        // -3. 获取环境信息
        // 获取系统环境变量
        String javaHome = context.getEnvironment().getProperty("JAVA_HOME");
        System.out.println(javaHome);
        // 获取配置文件变量
        String applicationName = context.getEnvironment().getProperty("spring.application.name");
        System.out.println(applicationName);

        // -4. 发布事件对象
        context.publishEvent(new UserRegisteredEvent(context));
        context.getBean(Component1.class).register();


        /*
            4. 学到了什么
                a. BeanFactory 与 ApplicationContext 并不是简单接口继承的关系，ApplicationContext 组合并扩展了 BeanFactory 的功能
                b. 新的代码之间解耦途径 —— 事件发布机制
         */

    }


}
